/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.diagram.activity.editpart;

import java.util.Iterator;

import org.eclipse.draw2d.XYLayout;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.commands.CompoundCommand;
import org.eclipse.gef.requests.ChangeBoundsRequest;
import org.eclipse.uml2.uml.ActivityParameterNode;

import com.cea.papyrus.core.commands.GraphNodeSetConstraintCommand;
import com.cea.papyrus.core.editpart.LayoutEditPolicy;
import com.cea.papyrus.core.helper.NodeHelper;
import com.cea.papyrus.core.utils.LookForElement;
import com.cea.papyrus.diagram.activity.command.UMLDi2ElementCreateFactory;
import com.cea.papyrus.diagram.activity.helper.NodeActivityParameterNodeHelper;
import com.cea.papyrus.diagraminterchange2.di2.DiagramElement;
import com.cea.papyrus.diagraminterchange2.di2.GraphElement;
import com.cea.papyrus.diagraminterchange2.di2.GraphNode;
import com.cea.papyrus.diagraminterchange2.di2.Uml1SemanticModelBridge;

/**
 * Layout edit policy for the composite diagram.
 */
public class ActivityDiagramLayoutEditPolicy extends LayoutEditPolicy {

	public ActivityDiagramLayoutEditPolicy(XYLayout layout) {
		super(layout);
	}

	/**
	 * Return specific factory to provide specific commands
	 */
	@Override
	protected UMLDi2ElementCreateFactory getCreateCommandFactory() {
	    return UMLDi2ElementCreateFactory.eINSTANCE;
	}
	
	/**
	 * used to create an add command.
	 * 
	 * @param childEditPart the editpart that is conecerned
	 * @param constraint to set on this editpart
	 * @param request to create an add command
	 * 
	 * @return the addcommand
	 */
	protected Command createAddCommand(ChangeBoundsRequest request, 	EditPart childEditPart, Object constraint) {
		CompoundCommand c = new CompoundCommand();
		if (childEditPart instanceof ActivityParameterNodeEditPart) {
			GraphElement parentGE = (GraphElement) this.getHost().getModel();
			
			Iterator<DiagramElement> itgraphChild = ((ActivityParameterNodeEditPart) childEditPart).getGraphNode().getSemanticChild().iterator();
			while (itgraphChild.hasNext()) {
				GraphNode gn = (GraphNode) itgraphChild.next();
				Rectangle r = new Rectangle(gn.getPosition().x, gn
						.getPosition().y, gn.getSize().width,
						gn.getSize().height);
				GraphNodeSetConstraintCommand locationCommand = new GraphNodeSetConstraintCommand(
						gn, request, request.getTransformedRectangle(r));
				c.add(locationCommand);
			}
			
			if (((ActivityParameterNodeEditPart) childEditPart).getGraphNode().getSemanticParent() != parentGE) {
				return null;
			}
		}
		c.add(super.createAddCommand(request, childEditPart, constraint));

		return c;
	}

	/**
	 * this method is used to set a nex position of the graphNode when the
	 * parent GraphNode has moved.
	 * 
	 * @param parentGraphNode the parentgraphNode that have moved.
	 * @param request  the change Bound Request that the parent graphNode has received.
	 * @param portGraphNode the graphNode thath represent the port or the label of the  GraphNode
	 * 
	 * @return Rectangle that respresent good position and size of the port
	 */
	public Rectangle calculateNewPosition(GraphNode parentGraphNode, GraphNode portGraphNode, ChangeBoundsRequest request) {
		Rectangle rec = new Rectangle();
		// detection of Mouvement
		if (request.getSizeDelta().equals(new Dimension(0, 0))) {
			// apply translation
			rec.setSize(portGraphNode.getSize());
			rec.setLocation(portGraphNode.getPosition());
			rec.x = rec.x + request.getMoveDelta().x;
			rec.y = rec.y + request.getMoveDelta().y;
			return rec;
		}
		// dectection of a resize
		else {
			// 1 calculus of intern limit.
			Rectangle smallParentRect = new Rectangle(parentGraphNode
					.getPosition().x
					+ request.getMoveDelta().x, parentGraphNode.getPosition().y
					+ request.getMoveDelta().y, parentGraphNode.getSize().width
					+ request.getSizeDelta().width,
					parentGraphNode.getSize().height + request.getSizeDelta().height);
			// caculus of extern limit
			Rectangle bigParentRect = new Rectangle(parentGraphNode.getPosition().x
					+ request.getMoveDelta().x - 1, parentGraphNode.getPosition().y
					+ request.getMoveDelta().y - 1,
					parentGraphNode.getSize().width
							+ request.getSizeDelta().width + 2, parentGraphNode
							.getSize().height + request.getSizeDelta().height + 2);

			// 2 calculus of the middle of the port
			Point portMiddlePosition = new Point(portGraphNode.getPosition().x
					+ portGraphNode.getSize().width / 2, portGraphNode.getPosition().y
					+ portGraphNode.getSize().height / 2);

			// 3 if the point is in the extern rectangle and not in the intern
			// limit the port is well placed
			if ((!smallParentRect.contains(portMiddlePosition))	&& bigParentRect.contains(portMiddlePosition)) {
				rec.setSize(portGraphNode.getSize());
				rec.setLocation(portGraphNode.getPosition());
				return rec;
			}
			// 4 the port is bad placed
			else {
				// creation of a future parentPoint
				NodeHelper nf = new NodeHelper();
				GraphNode futureGreatParent = nf.createGraphElement(null, null,	null, null);
				futureGreatParent.setPosition(LookForElement.calculateAbsolutePosition((GraphNode)parentGraphNode.getContainer(), new Point(0, 0)));
				GraphNode futureparent = nf.createGraphElement(null, null,null, null);
				futureparent.setPosition(smallParentRect.getTopLeft());
				futureparent.setSize(smallParentRect.getSize());
				futureGreatParent.getContained().add(futureparent);

				NodeActivityParameterNodeHelper np = new NodeActivityParameterNodeHelper();
				Point goodPosition = np.inRunner(futureparent, portGraphNode.getPosition(), portGraphNode.getSize());
				rec.setLocation(goodPosition);
				rec.setSize(portGraphNode.getSize());
				return rec;
			}
		}
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.common.editpart.LayoutEditPolicy#createChangeConstraintCommand(org.eclipse.gef.requests.ChangeBoundsRequest, org.eclipse.gef.EditPart, java.lang.Object)
	 */
	@Override
	protected Command createChangeConstraintCommand(ChangeBoundsRequest request, EditPart child, Object constraint) {
		GraphNodeSetConstraintCommand locationCommand;
		CompoundCommand command = new CompoundCommand();

		// first move the conecerned Editpart
		locationCommand = new GraphNodeSetConstraintCommand((GraphNode) child.getModel(), request, (Rectangle) constraint);
		command.add(locationCommand);

		// if this class move owned ports
		if (child instanceof ActivityEditPart) {

			GraphNode parentGraphNode = ((com.cea.papyrus.core.editpart.GraphNodeWithSemanticEditPart) child).getGraphNode();
			// itgn iterator of graphNode
			Iterator<DiagramElement> itgn = parentGraphNode.getSemanticChild().iterator();

			while (itgn.hasNext()) {
				GraphElement ge = (GraphElement) itgn.next();
				if (((Uml1SemanticModelBridge) ge.getSemanticModel()).getElement() instanceof ActivityParameterNode) {
					// ge is a graphNode of a port
					GraphNode portgn = (GraphNode) ge;
					// calculate new position of the port
					Rectangle rec = calculateNewPosition(parentGraphNode, portgn, request);
					locationCommand = new GraphNodeSetConstraintCommand(portgn,	request, rec);

					// add this command in the list of commands
					command.add(locationCommand);
					// look for all portNameEditPArt graphNode
					Iterator<DiagramElement> itPortName = portgn.getSemanticChild().iterator();
					Dimension diff = rec.getLocation().getDifference(portgn.getPosition());
					// look for all graphNode that is connected to propertygn
					while (itPortName.hasNext()) {
						GraphNode gePortName = (GraphNode) itPortName.next();
						// Create a move command for this element
						rec = new Rectangle(gePortName.getPosition(),gePortName.getSize());
						rec = rec.getTranslated(diff.width, diff.height);

						locationCommand = new GraphNodeSetConstraintCommand((GraphNode) gePortName, request, rec);
						// add this command in the list of commands
						command.add(locationCommand);
					}
				}
			}
			displayCompoundCommand(command);
			return command;
		}// end of part

		// case of a port
		if (child instanceof ActivityParameterNodeEditPart) {
			ActivityParameterNodeEditPart porteditpart = (ActivityParameterNodeEditPart) child;
			GraphNode portgn = (porteditpart).getGraphNode();

			// look for all portNameEditPArt graphNode
			Iterator<DiagramElement> itgn = portgn.getSemanticChild().iterator();

			// look for all gephNode that is connected to propertygn
			while (itgn.hasNext()) {
				GraphElement ge = (GraphElement) itgn.next();
				// Create a move command for this element
				// calculate new position of the port
				Rectangle rec = new Rectangle();
				rec.x = ((GraphNode) ge).getPosition().x
						+ request.getMoveDelta().x
						+ request.getSizeDelta().width;
				rec.y = ((GraphNode) ge).getPosition().y
						+ request.getMoveDelta().y
						+ request.getSizeDelta().height;
				rec.setSize(((GraphNode) ge).getSize());
				locationCommand = new GraphNodeSetConstraintCommand((GraphNode) ge, request, rec);
				command.add(locationCommand);
			}
			return command;
		}
		return super.createChangeConstraintCommand(request, child, constraint);
	}
}